package com.supermap.redis.service;

import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import javax.servlet.http.HttpServletRequest;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import com.supermap.mysql.tool.MysqlEntity;
import com.supermap.redis.tool.RedisDtat;
import com.supermap.redis.tool.RedisToo;
import com.supermap.tool.StringUtil;


/**
 * @author yuyao
 * @ClassName: WebControllerAop
 * @Description: AOP 实现缓存操作  mysql
 * @date 2017年12月29日 上午11:25:01
 */
@Component
@Aspect
public class MysqlControllerAop {

    //缓存 是否更新全局开关
    volatile static boolean updateqjkg = true;

    @Autowired
    RedisToo redisToo = new RedisToo();

    //log4j2 日子管理
    protected final Logger logger = LoggerFactory.getLogger(this.getClass());

    //匹配com.supermap.mysql.controller包及其子包下的所有类的所有方法
    @Pointcut("execution(* com.supermap.mysql.controller..*.*(..))")
    public void executeService() {

    }

    /**
     * 后置异常通知
     * 定义一个名字，该名字用于匹配通知实现方法的一个参数名，当目标方法抛出异常返回后，将把目标方法抛出的异常传给通知方法；
     * throwing 限定了只有目标方法抛出的异常与通知方法相应参数异常类型时才能执行后置异常通知，否则不执行，
     * 对于throwing对应的通知方法参数为Throwable类型将匹配任何异常。
     *
     * @param joinPoint
     * @param exception
     */
    @AfterThrowing(value = "executeService()", throwing = "exception")
    public void doAfterThrowingAdvice(JoinPoint joinPoint, Throwable exception) {
        //目标方法名：
        logger.error(joinPoint.getSignature().getName() + "发生了异常!!!!!" + exception.getMessage());
    }

    /**
     * @param @param  proceedingJoinPoint
     * @param @return 设定文件
     * @return Object    返回类型
     * @throws
     * @Title: doAroundAdvice
     * 环绕通知：
     * 决定目标方法是否执行，什么时候执行，执行时是否需要替换方法参数，执行完毕是否需要替换返回值。
     * 环绕通知第一个参数必须是org.aspectj.lang.ProceedingJoinPoint类型
     */
    @Around("execution(* com.supermap.mysql.controller..*.*(..))")
    public Object doAroundAdvice(ProceedingJoinPoint proceedingJoinPoint) {

        //获取到 httpServletRequest
        HttpServletRequest httpServletRequest = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        //获取封装 参数
        MysqlEntity mysqlentity = getMysqlEntity(httpServletRequest);
        boolean gxhc = isRecomputeCache(mysqlentity);
        if (gxhc) {
            mysqlentity.setUpdate(false);
            //删除对应的 缓存数据
            workXcDelHcdata(mysqlentity.getWarehouse() + mysqlentity.getTableName());
        }
        //获取 到 数据 映射key
        String key = getKet(mysqlentity);
        Object obj;
        //缓存查询
        if (mysqlentity.isUpdate() && updateqjkg && key != null) {
            try {
                RedisDtat hcdata = redisToo.findOne(key);
                if (hcdata != null && hcdata.getData() != null) {
                    logger.info("缓存获取数据！");
                    return hcdata.getData();
                }
                obj = getProceed(proceedingJoinPoint, mysqlentity, key);
            } catch (Exception e) {
                updateqjkg = false;
                obj = getProceed(proceedingJoinPoint, mysqlentity, key);
                return obj == null ? getMsg() : obj;
            }
        } else {
            obj = getProceed(proceedingJoinPoint, mysqlentity, key);
            return obj == null ? getMsg() : obj;
        }
        return obj == null ? getMsg() : obj;
    }

    /**
     * @param @param  proceedingJoinPoint
     * @param @return 设定文件
     * @return Object    返回类型
     * @throws
     * @Title: getProceed
     * @Description: 调用目标方法
     */
    public Object getProceed(ProceedingJoinPoint proceedingJoinPoint, MysqlEntity mysqlentity, String key) {
        //缓存数据
        RedisDtat rds = new RedisDtat();
        try {
            //调用执行目标方法  获取 执行结果
            Object obj = proceedingJoinPoint.proceed();
            //更新不做缓存操作
            if (updateqjkg && mysqlentity.isUpdate()) {
                //加入缓存
                rds.setId(key);
                rds.setData(obj);
                //添加缓存映射 语句
                saveKey(mysqlentity.getWarehouse() + mysqlentity.getTableName(), key);
                //添加缓存映射 数据
                redisToo.save(rds);
            }
            logger.info("数据库获取数据！");
            return obj;
        } catch (Throwable throwable) {
            logger.error("目标方法调用异常，方法名：" + proceedingJoinPoint.getSignature().getName() + throwable.getMessage(), throwable);
        }
        return null;
    }

    /**
     * @param @param  httpServletRequest
     * @param @return 设定文件
     * @return String    返回类型
     * @throws
     * @Title: getMD5Key
     * @Description:获取 查询参数 进行封装
     */
    public MysqlEntity getMysqlEntity(HttpServletRequest httpServletRequest) {

        MysqlEntity mysqlentity = new MysqlEntity();

        //获取请求参数
        Enumeration<String> names = httpServletRequest.getParameterNames();

        while (names.hasMoreElements()) {
            String name = (String) names.nextElement();
            String value = httpServletRequest.getParameter(name).replaceAll(" ", "");
            ;
            if ("warehouse".equals(name)) {
                mysqlentity.setWarehouse(value);
            } else if ("tableName".equals(name)) {
                mysqlentity.setTableName(value);
            } else if ("update".equals(name)) {
                mysqlentity.setUpdate(new Boolean(value).booleanValue());
            } else if ("sql".equals(name)) {
                mysqlentity.setSql(value);
                ;
            } else if ("conditionValue".equals(name)) {
                mysqlentity.setConditionValue(value);
                ;
            } else if ("updateValue".equals(name)) {
                mysqlentity.setUpdateValue(value);
                ;
            }
        }
        return mysqlentity;
    }

    /**
     * @param @param  mysqlentity
     * @param @return 设定文件
     * @return boolean    返回类型
     * @throws
     * @Title: isRecomputeCache
     * @Description: 是否重新计算缓存
     */
    public boolean isRecomputeCache(MysqlEntity mysqlentity) {
        if (mysqlentity.isUpdate()) {
            if (StringUtil.isNotNull(mysqlentity.getSql())) {
                String sud = mysqlentity.getSql().substring(0, 6);
                if ("delete".equalsIgnoreCase(sud)) return true;
                ;
                if ("update".equalsIgnoreCase(sud)) return true;
                ;
            }
            if (StringUtil.isNotNull(mysqlentity.getUpdateValue()) && !"{}".equals(mysqlentity.getUpdateValue())) {
                return true;
            }
        }
        return false;
    }

    /**
     * @param @param  mysqlentity
     * @param @return 设定文件
     * @return String    返回类型
     * @throws
     * @Title: getKet
     * @Description: 获取缓存映射 数据的 key
     */
    public String getKet(MysqlEntity mysqlentity) {
        String key = null;
        try {
            key = StringUtil.EncoderByMd5(mysqlentity.getWarehouse() + mysqlentity.getTableName() + mysqlentity.getSql());
	    	 /*if(mysqlentity.isUpdate()) {
   	    		//删除对应的 缓存数据
   	    		 workXcDelHcdata(mysqlentity.getWarehouse()+"*"+mysqlentity.getTableName());
   	    	 }*/
        } catch (Exception e) {
            logger.error("MD5转换 出现异常，转换参数：" + mysqlentity.getSql());
        }
        logger.info("请求数据库名:" + mysqlentity.getWarehouse() + " 请求表:" + mysqlentity.getTableName() + " 生成缓存KEY：" + key);
        return key;
    }

    /**
     * @param @param key
     * @param @param val    设定文件
     * @return void    返回类型
     * @throws
     * @Title: saveKey
     * @Description: 保存对应的key
     */
    @SuppressWarnings("unchecked")
    public void saveKey(String key, String val) {

        //查询映射
        RedisDtat cxys = new RedisDtat();

        ArrayList<String> list = new ArrayList<String>();

        if (redisToo.findOne(key) == null) {
            list.add(val);
        } else {
            list = (ArrayList<String>) redisToo.findOne(key).getData();
            list.add(val);
        }

        //cxys.setCacheSeconds(-1);//永不过期
        cxys.setId(key);
        cxys.setData(list);
        redisToo.save(cxys);

    }

    /**
     * @param @param key    设定文件
     * @return void    返回类型
     * @throws
     * @Title: workXc
     * @Description: 新建一个线程处理  删除缓存数据
     */
    public void workXcDelHcdata(final String mapkey) {
        Thread thread = new Thread() {
            public void run() {
                if (updateqjkg) deleteHcsj(mapkey);
            }
        };
        thread.start();
    }

    /**
     * @param mapkey 设定文件
     * @return void    返回类型
     * @throws
     * @Title: deleteHcsj
     * @Description: 清空对应表的缓存数据
     */
    @SuppressWarnings("unchecked")
    public void deleteHcsj(String mapkey) {
        updateqjkg = false;
        //RedisDtat data = redisToo.findOne(mapkey);
        List<Object> listCx = redisToo.likeAll(mapkey);
        for (Object cx : listCx) {
            RedisDtat data = (RedisDtat) cx;
            if (data != null) {
                ArrayList<String> li1s = (ArrayList<String>) data.getData();
                if (li1s != null && li1s.size() != 0) {
                    for (String i : li1s) {
                        redisToo.delete(i);
                        logger.info("清空缓存数据！被清除的KEY：" + i);
                    }
                    redisToo.delete(data.getId());
                    logger.info("清空缓存查询数据！被清除的KEY：" + data.getId());
                }
            }
        }
        updateqjkg = true;
    }

    /**
     * @param @return 设定文件
     * @return FhData    返回类型
     * @throws
     * @Title: getMsg
     * @Description: 返回信息
     */
    public HashMap<String, String> getMsg() {
        HashMap<String, String> msg = new HashMap<String, String>();
        msg.put("cod", "2");
        msg.put("msg", "查询错误！请检查查询条件！");
        msg.put("time", StringUtil.getSystemDateTimeString());
        return msg;
    }

    public static void setUpdateqjkg(boolean updateqjkg) {
        MysqlControllerAop.updateqjkg = updateqjkg;
    }


}
